﻿using System;
using System.Net.Sockets;
using System.IO;
using System.Net;
using System.Collections;
using System.Threading.Tasks;

namespace CzfSDK
{
	public class CzfTcpClient:CzfNetClient
	{
		public bool closed = false;

		const int HEAD_LENGTH = 4;
		public Socket socket;
		byte[] bts = new byte[1024];

		public CzfTcpClient (Socket socket)
		{
			this.socket = socket;
			try{
				remoteEndPoint = socket.RemoteEndPoint.ToString ();
			}catch{
				// 如果对方建立连接后立刻断开连接,则RemoteEndPoint会抛出异常
				remoteEndPoint = "unkown";
			}
			lastRecive = DateTime.Now;

		}

		#region Send
		MemoryStream sendMs = new MemoryStream ();
//		int readPos = 0;
//		bool sending = false;
//		object send_lock = new object();

		public override void Send (byte[] bytes)
		{
			base.Send (bytes);
			BeginSend (bytes);
		}

		void BeginSend(byte[] data) {
			if (socket == null || !socket.Connected)
				return;

			byte[] lenBytes = GetBytesByInt (data.Length);
			sendMs = new MemoryStream ();
			sendMs.Write (lenBytes, 0, lenBytes.Length);
			sendMs.Write (data, 0, data.Length);
			socket.BeginSend (sendMs.GetBuffer (), 0, (int)sendMs.Position, SocketFlags.None, EndSend, (int)sendMs.Position);
		}

		void EndSend (IAsyncResult ar)
		{
			int needSend = (int)ar.AsyncState;
			int len = 0;
			try{
				len = socket.EndSend (ar);
				if(len != needSend){
					Console.WriteLine("Send error:" + len + "|" + needSend);
				}
			}catch{
				Close ();
				return;
			}
		}

//		public void BeginSend(byte[] data) {
//			if (!socket.Connected)
//				return;
//
//			byte[] lenBytes = GetBytesByInt (data.Length);
//			lock (send_lock) {
//				sendMs.Write (lenBytes, 0, lenBytes.Length);
//				sendMs.Write (data, 0, data.Length);
//				if (!sending) {
//					sending = true;
//					socket.BeginSend (sendMs.GetBuffer (), readPos, (int)sendMs.Position - readPos, SocketFlags.None, EndSend, null);
//				}
//			}
//		}
//
//		void EndSend (IAsyncResult ar)
//		{
//			lock (send_lock) {
//				int len = 0;
//				try{
//					len = socket.EndSend (ar);
//				}catch{
//					CloseSocket ();
//					return;
//				}
//				readPos += len;
//
//				if (readPos == sendMs.Position) {
//					readPos = 0;
//					sendMs.Position = 0;
//					sending = false;
//				} else {
//					socket.BeginSend (sendMs.GetBuffer (), readPos, (int)sendMs.Position - readPos, SocketFlags.None, EndSend, null);
//				}
//			}
//		}
		#endregion

		#region Recive
		public override void BeginRecive()
		{
			if(socket != null)
				socket.BeginReceive (bts, 0, bts.Length, SocketFlags.None, EndRecive, null);
		}

		// Head
		byte[] dataLenBytes = new byte[HEAD_LENGTH];
		int dataLenBytesPos = 0;

		// Data
		MemoryStream dataBytes = null;

		int dataLength = 0;

		void EndRecive (IAsyncResult ar)
		{
			ThreadTool.LogThread ();
			int len = 0;
			try{
				len = socket.EndReceive(ar);
			}catch{
				Close ();
				return;
			}
			if (len == 0) {
				Close ();
				return;
			}

			int readPos = 0;
			while (readPos < len) {
				if (dataBytes == null) {
					dataLenBytes [dataLenBytesPos++] = bts [readPos++];
					if (dataLenBytesPos == dataLenBytes.Length) {
						dataLength = GetIntByBytes (dataLenBytes);
						dataBytes = new MemoryStream();
						dataLenBytesPos = 0;
					}
				} else {
					dataBytes.WriteByte(bts[readPos++]);
					if (dataLength == dataBytes.Position) {
						ReciveData(dataBytes.ToArray());
						dataBytes = null;
					}
				}
			}

			BeginRecive ();
		}

		void ReciveData(byte[] data)
		{
			lastRecive = DateTime.Now;
			if(OnRecive != null)
				OnRecive (this, data);
		}
		#endregion

		int GetIntByBytes(byte[] bytes)
		{
			int ret = 0;
			for (int i=0; i<bytes.Length; i++) {
				ret += bytes[bytes.Length - i - 1] << (i * 8);
			}
			return ret;
		}

		byte[] GetBytesByInt(int len)
		{
			byte[] bytes = new byte[HEAD_LENGTH];
			for (int i=0; i<HEAD_LENGTH; i++) {
				bytes[bytes.Length - i - 1] = (byte)(len >> (i * 8));
			}
			return bytes;
		}

		public override void Close()
		{
			try{
				if(socket != null){
					socket.Shutdown(SocketShutdown.Both);
					socket.Close ();
					socket = null;
				}
			}catch{
			}
			if (!closed) {
				closed = true;
				if (OnClose != null)
					OnClose (this);
			}
		}
	}
}

